﻿using Digitalmes.Model;
using Digitalmes.Models;
using Digitalmes.Parameter;
using Opc.Ua;
using Opc.Ua.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Digitalmes.Services.Communication
{
    public class OpcUaDriver : BaseDriver,ITransient
    {
        private Session _session;

        private Timer _reconnectTimer;
        private const int RECONNECT_INTERVAL = 5000;
        private Subscription _subscription;
        private const int PUBLISH_INTERVAL = 1000;

        private readonly Dictionary<string, MonitoredItem> _tagMonitors =
            new Dictionary<string, MonitoredItem>();

        public OpcUaDriver(IServiceProvider provider)
            : base(provider) 
        {
          
        }

        public override async Task<ServiceResponse> ConnectAsync(DriverParameter para)
        {
          
            using (await _mutex.LockAsync())
            {
                this.Parameter=para;    
                _port = para.Port;
                _host = para.IpAddress;
                _protocol=para.DriverType;
                
                para.Driver = this;
                var reponse = new ServiceResponse();
                reponse.Success = true;
                await Task.Delay(10);
                if (IsConnected) return reponse;

                try
                {
                    // 确保只创建一次连接
                    if (_session != null && _session.Connected)
                    {
                        await SetStatus(DriverStatus.Connected);
                        return reponse;
                    }

                    var endpoint = $"opc.tcp://{Host}:{Port}";

                    // 创建应用配置
                    var config = new ApplicationConfiguration
                    {
                        ApplicationName = "Xics.Designer",
                        ApplicationUri = $"urn:{System.Net.Dns.GetHostName()}:Xics.Designer",
                        ApplicationType = ApplicationType.Client,
                        SecurityConfiguration = new SecurityConfiguration
                        {
                            ApplicationCertificate = new CertificateIdentifier { StoreType = "Directory" },
                            TrustedPeerCertificates = new CertificateTrustList { StoreType = "Directory" },
                            RejectedCertificateStore = new CertificateTrustList { StoreType = "Directory" },
                            AutoAcceptUntrustedCertificates = true
                        },
                        TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
                        ClientConfiguration = new ClientConfiguration()
                    };

                    // 验证配置
                    config.Validate(config.ApplicationType);

                    //await config.Validate(config.ApplicationType).ConfigureAwait(false);

                    // 选择端点
                    var endpointDescription = CoreClientUtils.SelectEndpoint(endpoint, false);
                    var endpointConfiguration = EndpointConfiguration.Create(config);
                    var endpointUrl = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);

                    // 创建会话
                    _session = Session.Create(
                        config,
                        endpointUrl,
                        true,
                        false,
                        config.ApplicationName,
                        60000,
                        new UserIdentity(),
                        null
                    ).Result;

                    // 创建订阅
                    reponse = CreateSubscription();


                    if (para.MonitorTags != null)
                    {
                        foreach ( var tag in para.MonitorTags )
                        {
                            RegisterForPolling($"ns=2;s={tag.Address}", 100);
                        }
                    }

                    await SetStatus(DriverStatus.Connected);
                    return reponse;
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"OPC UA连接错误: {ex.Message}");
                    await SetStatus(DriverStatus.Error);
                    StartReconnectTimer();
                    reponse.Success = false;
                    reponse.Exception = ex;
                    Log.Error($"OPC UA连接错误: {ex.Message}", ex);
                    return reponse;
                }
            }
        }

        private ServiceResponse CreateSubscription()
        {
            var response = new ServiceResponse();
            try
            {
                if (_session == null || !_session.Connected)
                {
                    response.Success = false;
                    return response;
                }

                _subscription = new Subscription(_session.DefaultSubscription)
                {
                    PublishingInterval = PUBLISH_INTERVAL,
                    DisplayName = "XicsSubscription"
                };

                _session.AddSubscription(_subscription);
                _subscription.Create();
                _subscription.PublishStatusChanged += OnPublishComplete;
                response.Success = true;
            }
            catch (Exception ex)
            {
                response.Exception = ex;
                Log.Error("Opc创建订阅失败", ex);
            }
            return response;
        }

        private void OnPublishComplete(object sender, EventArgs e)
        {
            try
            {
                // 处理数据变化通知
                foreach (var item in _subscription.MonitoredItems)
                {
                    var datachanges = item.DequeueValues();
                    foreach (var value in datachanges)
                    {
                        if (value.Value != null && item.Handle is string tag)
                        {
                            _tagCache[tag] = value.Value;
                            OnTagValueChanged(tag, value.Value);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"处理OPC UA数据变化错误: {ex.Message}");
            }
        }

        private void StartReconnectTimer()
        {
            _reconnectTimer?.Dispose();
            _reconnectTimer = new Timer(async _ =>
            {
                try
                {
                    var response = await ConnectAsync(Parameter);

                    if (response.Success)
                    {
                        _reconnectTimer?.Dispose();
                        _reconnectTimer = null;
                    }
                }
                catch (Exception ex)
                {
                    Log.Error($"Opc重连失败", ex);
                }
            }, null, RECONNECT_INTERVAL, Timeout.Infinite);
        }

        public override async Task<ServiceResponse> Disconnect()
        {
            using (await _mutex.LockAsync())
            {
                var response = new ServiceResponse();
                try
                {
                    if (_subscription != null)
                    {
                        _subscription.PublishStatusChanged -= OnPublishComplete;
                        _subscription.Delete(true);
                        _subscription.Dispose();
                        _subscription = null;
                    }

                    if (_session != null)
                    {
                        _session.Close();
                        _session.Dispose();
                        _session = null;
                    }

                    //_monitoredItems
                    _tagMonitors.Clear();
                    response.Success = true;
                    _reconnectTimer?.Dispose();
                    await SetStatus(DriverStatus.Disconnected);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"OPC UA断开连接错误: {ex.Message}");
                    await SetStatus(DriverStatus.Error);
                    response.Success = false;
                    response.Exception = ex;
                    Log.Error($"OPC UA断开连接错误: {ex.Message}", ex);
                }
                return response;
            }
        }


          void ReadTags(List<ISysTag> tags)
          {
        
               
                try
                {
                    if (!IsConnected)
                    {
                      
                    }

                    var tagList = tags.Select(x=>x.Address).ToList();
                   
                    // 分批读取（OPC UA支持单次读取多个节点）
                    const int BATCH_SIZE = 100;
                    for (int i = 0; i < tagList.Count; i += BATCH_SIZE)
                    {
                        var batch = tagList.Skip(i).Take(BATCH_SIZE).ToList();
                        var nodesToRead = new ReadValueIdCollection();

                        foreach (var stag in batch)
                        {
                            nodesToRead.Add(new ReadValueId
                            {
                                NodeId = new NodeId($"ns=2;s={stag}"),
                                AttributeId = Opc.Ua.Attributes.Value,  // 使用正确的属性ID
                                Handle = stag
                            });
                        }

                        if (_session == null || !_session.Connected)
                        {
                           
                         
                        }

                        _session.Read(
                             null,
                             0,
                             TimestampsToReturn.Both,
                             nodesToRead,
                             out var resultsArray,
                             out var diagnosticInfos
                         );

                        ValidateResponse(resultsArray, batch);

                        for (int j = 0; j < batch.Count; j++)
                        {
                            var value = resultsArray[j].Value;
                            tags[j].Value= value;
                            _tagCache[batch[j]] = value;
                        }
                    }

                 
                }
                catch (Exception ex)
                {                  
                    Log.Error("读取失败", ex);
                }
              
            
          }


        public override async Task<ServiceResponse<Dictionary<string, object>>> ReadValueAsync(IEnumerable<string> tags)
        {
            using (await _mutex.LockAsync())
            {
                var response = new ServiceResponse<Dictionary<string, object>>();
                try
                {
                    if (!IsConnected)
                    {
                        response.Success = false;
                        response.Message = "OPC UA驱动未连接";
                        return response;
                    }

                    var tagList = tags.ToList();
                    var results = new Dictionary<string, object>();

                    // 分批读取（OPC UA支持单次读取多个节点）
                    const int BATCH_SIZE = 100;
                    for (int i = 0; i < tagList.Count; i += BATCH_SIZE)
                    {
                        var batch = tagList.Skip(i).Take(BATCH_SIZE).ToList();
                        var nodesToRead = new ReadValueIdCollection();

                        foreach (var tag in batch)
                        {
                            nodesToRead.Add(new ReadValueId
                            {
                                NodeId = new NodeId(tag),
                                AttributeId = Opc.Ua.Attributes.Value,  // 使用正确的属性ID
                                Handle = tag
                            });
                        }

                        if (_session == null || !_session.Connected)
                        {
                            response.Message = "会话不可用";
                            response.Success = false;
                            return response;
                        }

                        _session.Read(
                             null,
                             0,
                             TimestampsToReturn.Both,
                             nodesToRead,
                             out var resultsArray,
                             out var diagnosticInfos
                         );

                        ValidateResponse(resultsArray, batch);

                        for (int j = 0; j < batch.Count; j++)
                        {
                            var value = resultsArray[j].Value;
                            results[batch[j]] = value;
                            _tagCache[batch[j]] = value;
                        }
                    }

                    response.Success = true;
                    response.Result = results;
                }
                catch (Exception ex)
                {
                    response.Success = false;
                    response.Exception = ex;
                    Log.Error("读取失败", ex);
                }
                return response;
            }
        }

        private void ValidateResponse(DataValueCollection values, List<string> tags)
        {
            for (int i = 0; i < values.Count; i++)
            {
                if (StatusCode.IsBad(values[i].StatusCode))
                {
                    throw new Exception($"读取标签'{tags[i]}'失败: {values[i].StatusCode}");
                }
            }
        }

        public override async Task<ServiceResponse> WriteValueAsync(Dictionary<string, object> tagValues)
        {
            using (await _mutex.LockAsync())
            {
                var response = new ServiceResponse();
                try
                {
                    if (!IsConnected)
                    {
                        response.Success = false;
                        response.Message = "OPC UA驱动未连接";
                        return response;
                    }

                    lock (_lockObj)
                    {
                        if (_session == null || !_session.Connected)
                            throw new InvalidOperationException("会话不可用");

                        var nodesToWrite = new WriteValueCollection();

                        foreach (var kvp in tagValues)
                        {
                            nodesToWrite.Add(new WriteValue
                            {
                                NodeId = new NodeId(kvp.Key),
                                AttributeId = Opc.Ua.Attributes.Value,  // 使用正确的属性ID
                                Value = new DataValue(new Variant(kvp.Value))
                            });
                        }

                        var result = _session.Write(
                            null,
                            nodesToWrite,
                            out var results,
                            out var diagnosticInfos
                        );

                        ValidateWriteResponse(results, tagValues.Keys.ToList());

                        foreach (var kvp in tagValues)
                        {
                            _tagCache[kvp.Key] = kvp.Value;
                            OnTagValueChanged(kvp.Key, kvp.Value);
                        }
                    }
                    response.Success = true;
                }
                catch (Exception ex)
                {
                    response.Success = false;
                    response.Message = ex.Message;
                    response.Exception = ex;
                    Log.Error("写入失败", ex);
                }
                return response;
            }
        }

        private void ValidateWriteResponse(StatusCodeCollection statusCodes, List<string> tags)
        {
            for (int i = 0; i < statusCodes.Count; i++)
            {
                if (StatusCode.IsBad(statusCodes[i]))
                {
                    throw new Exception($"写入标签'{tags[i]}'失败: {statusCodes[i]}");
                }
            }
        }

        public override void RegisterForPolling(string tag, int intervalMs)
        {
            lock (base._lockObj)
            {
                // 对于OPC UA，使用订阅机制替代轮询
                if (_subscription != null && _session != null && _session.Connected)
                {
                    if (!_tagMonitors.ContainsKey(tag))
                    {
                        var item = new MonitoredItem(_subscription.DefaultItem)
                        {
                            DisplayName = tag,
                            StartNodeId = new NodeId(tag),
                            SamplingInterval = intervalMs,
                            QueueSize = 10,
                            Handle = tag
                        };

                        item.Notification += OnMonitoredItemNotification;

                        _tagMonitors[tag] = item;

                        _subscription.AddItem(item);
                        _subscription.ApplyChanges();
                    }
                }
                else
                {
                    // 如果订阅不可用，回退到基础轮询
                    base.RegisterForPolling(tag, intervalMs);
                }
            }
        }

        private void OnMonitoredItemNotification(MonitoredItem item, MonitoredItemNotificationEventArgs e)
        {
            foreach (var value in item.DequeueValues())
            {
                if (value.Value != null && item.Handle is string tag)
                {
                    _tagCache[tag] = value.Value;
                    OnTagValueChanged(tag, value.Value);

                    foreach (var vb in Parameter.MonitorTags)
                    {
                        if (tag.Contains(vb.Address)  
                            && vb.TagGroup !=null
                            && vb.TagGroup.Count >=1)
                        {
                            ReadTags(vb.TagGroup);
                           _= SendSignal(vb);
                        }
                    }
                }
            }
        }

        public override void UnregisterForPolling(string tag)
        {
            lock (base._lockObj)
            {
                if (_tagMonitors.TryGetValue(tag, out var item))
                {
                    item.Notification -= OnMonitoredItemNotification;
                    _subscription.RemoveItem(item);

                    _tagMonitors.Remove(tag);
                    _subscription.ApplyChanges();
                }
                else
                {
                    base.UnregisterForPolling(tag);
                }
            }
        }

        protected override void StartPolling()
        {
            // OPC UA主要使用订阅机制
            // 对于未注册订阅的标签，使用基础轮询
            base.StartPolling();
        }

        protected void Dispose(bool disposing)
        {
            _reconnectTimer?.Dispose();
        }

        public override async ValueTask DisposeAsync()
        {
            await base.DisposeAsync();
            await Disconnect();
        }

        public override ServiceResponse AddVariable(ISysTag variable)
        {
            var response = new ServiceResponse();

            if (!this._variables.ContainsKey(variable.Name))
            {
                _variables[variable.Name] = variable;
                response.Success = true;

                RegisterForPolling(variable.Address, 100);
            }

            return response;
        }

        public override ServiceResponse RemoveVariable(ISysTag variable)
        {
            var response = new ServiceResponse();

            if (this._variables.ContainsKey(variable.Name))
            {
                _variables.TryRemove(variable.Name, out var item);
                UnregisterForPolling(variable.Name);    
                response.Success = true;
            }

            return response;
        }

        public override async Task<ServiceResponse> ReadValueAsync(string address)
        {
            using (await _mutex.LockAsync())
            {
                var response = new ServiceResponse();

                var re = await this.ReadValueAsync(new[] { address });

                if (re.Success)
                {
                    response.Success = true;
                    response.Result = re.Result[address];
                }
                else
                {
                    response.Success = false;
                    response.Result = null;
                    response.Message = re.Message;
                    response.Exception = re.Exception;
                }

                return response;
            }
        }

        public override async Task<ServiceResponse> WriteValueAsync(string address, object value)
        {
            return await WriteValueAsync(new Dictionary<string, object>()
            {
                { address, value },
            });
        }
    }
}